package com.gao.flying.mq.connection;

import com.gao.flying.mq.util.Assert;
import com.gao.flying.mq.util.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.jms.Connection;
import javax.jms.Session;


public class JmsResource {

    private boolean frozen = false;
    private List<Session> sessions = new LinkedList<>();
    private List<Connection> connections = new LinkedList<>();
    private final Map<Connection, List<Session>> sessionsPerConnection = new HashMap<>();

    private static Logger logger = LoggerFactory.getLogger(JmsResource.class);

    public JmsResource() {

    }

    public JmsResource(Session session) {
        addSession(session);
        this.frozen = true;
    }

    public JmsResource(Connection connection, Session session) {
        addConnection(connection);
        addSession(session, connection);
        this.frozen = true;
    }

    public final void addConnection(Connection connection) {
        Assert.isTrue(!this.frozen, "Cannot add Connection because JmsResourceHolder is frozen");
        Assert.notNull(connection, "Connection must not be null");
        if (!this.connections.contains(connection)) {
            this.connections.add(connection);
        }
    }

    private final void addSession(Session session) {
        addSession(session, null);
    }

    public final void addSession(Session session, Connection connection) {
        Assert.isTrue(!this.frozen, "Cannot add Session because JmsResourceHolder is frozen");
        Assert.notNull(session, "Session must not be null");
        if (!this.sessions.contains(session)) {
            this.sessions.add(session);
            if (connection != null) {
                List<Session> sessions = this.sessionsPerConnection.get(connection);
                if (sessions == null) {
                    sessions = new LinkedList<>();
                    this.sessionsPerConnection.put(connection, sessions);
                }
                sessions.add(session);
            }
        }
    }

    public boolean containsSession(Session session) {
        return this.sessions.contains(session);
    }

    public Connection getConnection() {
        return !this.connections.isEmpty() ? this.connections.get(0) : null;
    }

    public Connection getConnection(Class<? extends Connection> connectionType) {
        return CollectionUtils.findValueOfType(this.connections, connectionType);
    }

    public Session getSession() {
        return !this.sessions.isEmpty() ? this.sessions.get(0) : null;
    }

    public Session getSession(Class<? extends Session> sessionType) {
        return getSession(sessionType, null);
    }

    private Session getSession(Class<? extends Session> sessionType, Connection connection) {
        List<Session> sessions = this.sessions;
        if (connection != null) {
            sessions = this.sessionsPerConnection.get(connection);
        }
        return CollectionUtils.findValueOfType(sessions, sessionType);
    }

    public void closeAll() {
        for (Session session : this.sessions) {
            try {
                session.close();
            } catch (Throwable ex) {
                logger.error(ex.getMessage());
            }
        }
        for (Connection con : this.connections) {
            releaseConnection(con, true);
        }
        this.connections.clear();
        this.sessions.clear();
        this.sessionsPerConnection.clear();
    }

    private void releaseConnection(Connection con, boolean started) {
        if (con == null) {
            return;
        }
        if (started) {
            try {
                con.stop();
            } catch (Throwable ex) {
                logger.error(ex.getMessage());
            }
        }
        try {
            con.close();
        } catch (Throwable ex) {
            logger.error(ex.getMessage());
        }
    }

}
